//
// Created by tcj on 2022/3/21.
//

#ifndef __SYLAR__ADDRESS_H__
#define __SYLAR__ADDRESS_H__

#include "memory"
#include "string"
#include "sys/socket.h"
#include "sys/types.h"
#include "sys/un.h"
#include "arpa/inet.h"
#include "unistd.h"
#include "vector"
#include "iostream"
#include "map"

namespace sylar{
    class IPAdress;
    class Adress{
    public:
        typedef std::shared_ptr<Adress> ptr;

        static Adress::ptr Create(const sockaddr* addr,socklen_t addrlen);

        static bool Lookup(std::vector<Adress::ptr>& result,const std::string& host,
                           int family = AF_UNSPEC,int type=0,int protocol=0);

        static Adress::ptr LookupAny(const std::string& host,
                           int family = AF_UNSPEC,int type=0,int protocol=0);
         static std::shared_ptr<IPAdress> LookupAnyIpAddress(const std::string& host,
                           int family = AF_UNSPEC,int type=0,int protocol=0);

         static bool GetInterfaceAddresses(std::multimap<std::string,std::pair<Adress::ptr,uint32_t> >& result,
                                           int family = AF_UNSPEC);


         static bool GetInterfaceAddresses(std::vector<std::pair<Adress::ptr,uint32_t> >& result,
                           const std::string& iface, int family = AF_UNSPEC);


         virtual ~Adress(){}

        int getFamily() const;
        virtual const sockaddr* getAddr() const=0;
        virtual sockaddr* getAddr() =0;
        virtual socklen_t getAddrLen() const =0;

        virtual std::ostream& insert(std::iostream& os) const =0;
        std::string  toString() const;

        bool operator<(const Adress& rhs) const;
        bool operator==(const Adress& rhs) const;
        bool operator!=(const Adress& rhs) const;
    };


    class IPAdress : public Adress{
    public:
        typedef std::shared_ptr<IPAdress> ptr;

        static IPAdress::ptr Create(const char* address,uint16_t port=0);

        virtual IPAdress::ptr broadcastAdress(uint32_t prefix_len)=0;
        virtual IPAdress::ptr networkAdress(uint32_t prefix_len)=0;
        virtual IPAdress::ptr subnetMask(uint32_t prefix_len)=0;

        virtual uint32_t getPort() const =0;
        virtual void setPort(uint16_t v) =0;
    };

    class IPv4Adress : public IPAdress{
    public:
        typedef std::shared_ptr<IPv4Adress> ptr;

        static IPv4Adress::ptr Create(const char* address,uint16_t port=0);

        IPv4Adress(const sockaddr_in& address);
        IPv4Adress(uint32_t adress = INADDR_ANY,uint16_t port=0);


        const sockaddr* getAddr() const override;
         sockaddr* getAddr()  override;
        socklen_t getAddrLen() const override;
        std::ostream& insert(std::iostream& os) const override;

        IPAdress::ptr broadcastAdress(uint32_t prefix_len) override;
        IPAdress::ptr networkAdress(uint32_t prefix_len) override;
        IPAdress::ptr subnetMask(uint32_t prefix_len) override;

        uint32_t getPort() const override;
        void setPort(uint16_t v) override;

    private:
        sockaddr_in m_addr;
    };


    class IPv6Adress : public IPAdress{
    public:
        typedef std::shared_ptr<IPv6Adress> ptr;
        static IPv6Adress::ptr Create(const char* address,uint16_t port=0);
        IPv6Adress();
        IPv6Adress(const sockaddr_in6& address);
        IPv6Adress(const uint8_t address[16],uint16_t port=0);

        const sockaddr* getAddr() const override;
        sockaddr* getAddr()  override;
        socklen_t getAddrLen() const override;
        std::ostream& insert(std::iostream& os) const override;

        IPAdress::ptr broadcastAdress(uint32_t prefix_len) override;
        IPAdress::ptr networkAdress(uint32_t prefix_len) override;
        IPAdress::ptr subnetMask(uint32_t prefix_len) override;

        uint32_t getPort() const override;
        void setPort(uint16_t v) override;

    private:
        sockaddr_in6 m_addr;
    };


    class UnixAdress : public Adress{
    public:
        typedef std::shared_ptr<UnixAdress> ptr;
        UnixAdress();
        UnixAdress(const std::string& path);

        const sockaddr* getAddr() const override;
        sockaddr* getAddr() override;
        socklen_t getAddrLen() const override;
        void setAddrLen(uint32_t v);
        std::ostream& insert(std::iostream& os) const override;

    private:
        struct sockaddr_un m_addr;
        socklen_t m_length;
    };

    class UnknowAdress : public Adress{
    public:
        std::shared_ptr<UnknowAdress> ptr;
        UnknowAdress(int family);
        UnknowAdress(const sockaddr& addr);
        const sockaddr* getAddr() const override;
        sockaddr* getAddr()  override;
        socklen_t getAddrLen() const override;
        std::ostream& insert(std::iostream& os) const override;

    private:
        sockaddr m_addr;
    };

    std::ostream& operator<<(std::ostream& os,const Adress& addr);
}

#endif //SERVER_FRAMEWORK_ADDRESS_H
